package com.dd.ms.auth.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.dd.ms.auth.api.UserInfoService;
import com.dd.ms.auth.bo.UserInternalBO;
import com.dd.ms.auth.config.auth.JwtUtils;
import com.dd.ms.auth.mapper.UserMapper;
import com.dd.ms.auth.pojo.DO.RoleDO;
import com.dd.ms.auth.pojo.DO.UserDO;
import com.dd.ms.auth.pojo.dto.RolePermissionDTO;
import com.dd.ms.auth.pojo.resp.ApiKeyGeneResponse;
import com.dd.ms.auth.repository.RolePermissionRelRepository;
import com.dd.ms.auth.repository.UserRepository;
import com.dd.ms.auth.repository.UserRolesRelRepository;
import com.dd.ms.auth.service.ApiKeyService;
import com.dd.ms.auth.utils.converter.PermissionConverter;
import com.dd.ms.auth.utils.converter.RoleConverter;
import com.dd.ms.auth.utils.converter.UserConverter;
import com.dd.ms.auth.utils.redis.RedisKeyUtil;
import com.dd.ms.auth.vo.RoleVO;
import com.dd.ms.auth.vo.UserVO;
import com.dd.ms.common.exception.DomainException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.security.SecureRandom;
import java.time.Duration;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author liuxianmeng
 * @CreateTime 2025/7/23 19:35
 * @Description 类功能作用说明
 */
@Slf4j
@Service
@RequiredArgsConstructor
@DubboService(interfaceClass = UserInfoService.class, group = "DUBBO_DD_MS_AUTH", version = "1.0")
public class UserInfoServiceImpl implements UserInfoService, ApiKeyService {

    private final JwtUtils jwtUtils;
    private final RedisTemplate<String, Object> redisTemplate;
    private final UserRepository userRepository;
    private final UserRolesRelRepository userRolesRelRepository;
    private final RolePermissionRelRepository rolePermissionRelRepository;
    private final ConcurrentHashMap<String, Object> userInfoLocks = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, Object> userApiKeyLocks = new ConcurrentHashMap<>();
    private final UserMapper userMapper;

    @Override
    public UserVO getUserInfoByToken(String accessToken, Boolean permissionRequired) {
        try {
            if (!jwtUtils.validateAccessToken(accessToken)) {
                throw new DomainException(HttpStatus.UNAUTHORIZED.value(), "无效的访问令牌");
            }
        } catch (DomainException e) {
            if (e.getCode() == 401001) {
                throw e; // 直接抛出原始异常
            }
            throw new DomainException(HttpStatus.UNAUTHORIZED.value(), "无效的访问令牌");
        }

        String account = jwtUtils.getAccountFromToken(accessToken);
        return cacheQuery(account, permissionRequired, Duration.ofMinutes(30));
    }

    /**
     * 查询并缓存用户信息
     *
     * @param account 用户账户
     * @param permissionRequired 是否需要查询权限
     * @param ttl 缓存保留时间
     * @return 用户信息
     */
    private UserVO cacheQuery(String account, boolean permissionRequired, Duration ttl) {
        String key = permissionRequired
                ? RedisKeyUtil.getUserInfoFullKey(account)
                : RedisKeyUtil.getUserInfoProfileKey(account);

        // 先尝试无锁查询缓存
        UserVO userVO = (UserVO) redisTemplate.opsForValue().get(key);
        if (userVO != null) {
            return userVO;
        }

        // 使用专门的锁对象
        Object lock = userInfoLocks.computeIfAbsent(account, k -> new Object());
        synchronized (lock) {
            try {
                // 再次检查缓存
                userVO = (UserVO) redisTemplate.opsForValue().get(key);
                if (userVO != null) {
                    return userVO;
                }

                userVO = getUserInfo(account, permissionRequired);
                redisTemplate.opsForValue().set(key, userVO, ttl);
                return userVO;
            } finally {
                userInfoLocks.remove(account, lock);
            }
        }
    }

    private UserDO getUserDOByToken(String accessToken) {
        // 1 验证Token有效性（包括签名、过期时间等）
        if (!jwtUtils.validateAccessToken(accessToken)) {
            throw new DomainException(HttpStatus.UNAUTHORIZED.value(), "无效的访问令牌");
        }
        // 2 从Token中解析账户名（避免参数注入风险）
        String account = jwtUtils.getAccountFromToken(accessToken);
        return getUserDO(account);
    }

    @Override
    public String getUserApiKeyByToken(String token) {
        // 1 验证Token有效性（包括签名、过期时间等）
        if (!jwtUtils.validateAccessToken(token)) {
            throw new DomainException(HttpStatus.UNAUTHORIZED.value(), "无效的访问令牌");
        }
        // 2 从Token中解析账户名（避免参数注入风险）
        String userAccount = jwtUtils.getAccountFromToken(token);
        return cacheQuery(userAccount, Duration.ofMinutes(30));
    }

    @Override
    public UserInternalBO getUserInfoByAccessKey(String apiAccessKey) {
        LambdaQueryWrapper<UserDO> lqw = new LambdaQueryWrapper<>();
        lqw.eq(UserDO::getAccessKey, apiAccessKey);
        UserDO userDO = userMapper.selectOne(lqw);
        return UserConverter.DO2BO(userDO);
    }

    /**
     * 查询并缓存用户apiKey
     *
     * @param userAccount 用户账户
     * @param ttl 缓存保留时间
     * @return 用户apiKey
     */
    private String cacheQuery(String userAccount, Duration ttl) {
        String apiKeyRedisKey = RedisKeyUtil.getUserApiKeyRedisKey(userAccount);

        // 先尝试无锁查询缓存
        String apiKey = (String) redisTemplate.opsForValue().get(apiKeyRedisKey);
        if (apiKey != null) {
            return apiKey;
        }

        // 使用 ConcurrentHashMap 管理细粒度锁
        Object lock = userApiKeyLocks.computeIfAbsent(userAccount, k -> new Object());
        synchronized (lock) {
            try {
                // 再次检查缓存（Double-Check）
                apiKey = (String) redisTemplate.opsForValue().get(apiKeyRedisKey);
                if (apiKey != null) {
                    return apiKey;
                }

                // 查询数据库
                apiKey = userRepository.getUserApiKeyByAccount(userAccount);

                // 缓存结果（即使是 null，防止缓存穿透）
                if (apiKey != null) {
                    redisTemplate.opsForValue().set(apiKeyRedisKey, apiKey, ttl);
                }
                return apiKey;
            } catch (Exception e) {
                // 记录异常，避免影响业务
                log.error("Failed to cache query API key for account: " + userAccount, e);
                throw e;  // 或返回默认值，如 return "";
            } finally {
                // 清理锁，避免内存泄漏
                userApiKeyLocks.remove(userAccount, lock);
            }
        }
    }

    /**
     * 获取用户VO详细信息
     *
     * @param userAccount 用户账户名
     * @param permissionRequired 是否需要查询用户权限的详细信息
     * @return
     */
    private UserVO getUserInfo(String userAccount, Boolean permissionRequired) {
        UserDO userDO = getUserDO(userAccount);
        if (userDO == null) {
            throw new DomainException(HttpStatus.NOT_FOUND.value(), "用户不存在");
        }
        UserVO userVO = UserConverter.DO2VO(userDO);
        if (permissionRequired) {
            List<RoleVO> roleVOS = getUserRolesWithPermissions(userDO.getUserId());
            List<String> roleNames = roleVOS.stream()
                    .map(RoleVO::getRoleName)
                    .collect(Collectors.toList());
            userVO.setRolesList(roleNames);
            userVO.setRolesVOList(roleVOS);
        }
        return userVO;
    }

    private UserDO getUserDO(String account) {
        return userRepository.getUserByAccount(account);
    }

    /**
     * 获取用户角色VO详细信息
     *
     * @param userId 用户数据库id
     * @return
     */
    private List<RoleVO> getUserRolesWithPermissions(Long userId) {
        List<RoleDO> roles = userRolesRelRepository.getRolesByUserId(userId);
        if (CollectionUtils.isEmpty(roles)) {
            return Collections.emptyList();
        }

        List<Long> roleIds = roles.stream().map(RoleDO::getRoleId).collect(Collectors.toList());
        Map<Long, List<RolePermissionDTO>> permissionsMap = rolePermissionRelRepository
                .getPermissionsByRoleIds(roleIds)
                .stream()
                .collect(Collectors.groupingBy(RolePermissionDTO::getRoleId));

        return roles.stream().map(role -> {
            RoleVO vo = RoleConverter.toVO(role);
            List<RolePermissionDTO> permissions = permissionsMap.getOrDefault(role.getRoleId(), Collections.emptyList());
            vo.setPermissions(PermissionConverter.toVO(permissions));
            return vo;
        }).collect(Collectors.toList());
    }

    @Override
    public ApiKeyGeneResponse geneApiKey(String tokenStr) {
        UserDO userDO = getUserDOByToken(tokenStr);

        // 生成随机的 accessKey 和 secretKey
        SecureRandom random = new SecureRandom();
        byte[] accessKeyBytes = new byte[16];
        byte[] secretKeyBytes = new byte[32];
        random.nextBytes(accessKeyBytes);
        random.nextBytes(secretKeyBytes);

        String accessKey = Base64.getUrlEncoder().withoutPadding().encodeToString(accessKeyBytes);
        String secretKey = Base64.getUrlEncoder().withoutPadding().encodeToString(secretKeyBytes);

        // 更新 UserDO 对象
        userDO.setAccessKey(accessKey);
        userDO.setSecretKey(secretKey);

        // 使用 userRepository 保存更新后的用户信息
        userRepository.updateById(userDO);
        // 更新后删除RedisApiKey的缓存
        String apiKeyRedisKey = RedisKeyUtil.getUserApiKeyRedisKey(userDO.getAccount());
        redisTemplate.opsForValue().getOperations().delete(apiKeyRedisKey);

        // 构建并返回响应对象
        ApiKeyGeneResponse response = ApiKeyGeneResponse.builder()
                .accessKey(accessKey)
                .secretKey(secretKey)
                .build();

        return response;
    }
}
